import { isPowerOf2 } from './mathUtil'; // 假设你有这个工具

/**
 * WebGL 工具类（静态）
 * 提供着色器、缓冲区、纹理、帧缓冲、Uniform/Attribute 管理等常用功能
 */
export class GlUtil {
  static useProgram(gl: WebGL2RenderingContext, program: WebGLProgram) {
    gl.useProgram(program);
  }
  /**
   * 判断是否为 WebGL2 上下文
   */
  static isWebgl2(gl: WebGLRenderingContext): gl is WebGL2RenderingContext {
    return gl instanceof WebGL2RenderingContext;
  }

  /**
   * 创建着色器（顶点或片元）
   */
  static createShader(
    gl: WebGLRenderingContext,
    source: string,
    type: number
  ): WebGLShader | null {
    const shader = gl.createShader(type)!;
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      console.error('Shader compilation failed:', gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);
      return null;
    }
    return shader;
  }

  /**
   * 创建顶点着色器
   */
  static createVertexShader(
    gl: WebGLRenderingContext,
    source: string
  ): WebGLShader | null {
    return this.createShader(gl, source, gl.VERTEX_SHADER);
  }

  /**
   * 创建片元着色器
   */
  static createFragmentShader(
    gl: WebGLRenderingContext,
    source: string
  ): WebGLShader | null {
    return this.createShader(gl, source, gl.FRAGMENT_SHADER);
  }

  /**
   * 创建并链接程序
   */
  static createProgram(
    gl: WebGLRenderingContext,
    vertexShader: WebGLShader,
    fragmentShader: WebGLShader
  ): WebGLProgram {
    const program = gl.createProgram()!;
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      console.error('Program linking failed:', gl.getProgramInfoLog(program));
      throw new Error('Failed to link program');
    }

    return program;
  }

  /**
   * 创建 Program 并提取 uniforms/attributes 信息
   */
  static createProgramInfo(
    gl: WebGLRenderingContext,
    vertexSource: string,
    fragmentSource: string
  ) {
    const vShader = this.createVertexShader(gl, vertexSource);
    const fShader = this.createFragmentShader(gl, fragmentSource);
    if (!vShader || !fShader) throw new Error('Shader creation failed');

    const program = this.createProgram(gl, vShader, fShader);
    const uniforms = this.getUniforms(gl, program);
    const attributes = this.getAttributes(gl, program);

    return { program, uniforms, attributes };
  }

  /**
   * 获取程序中所有 active uniforms 的位置
   */
  static getUniforms(
    gl: WebGLRenderingContext,
    program: WebGLProgram
  ): { [key: string]: WebGLUniformLocation | null } {
    const num = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
    const uniforms: { [key: string]: WebGLUniformLocation | null } = {};
    for (let i = 0; i < num; i++) {
      const info = gl.getActiveUniform(program, i);
      if (info) {
        const name = info.name.replace(/\[0\]$/, ''); // 去除数组标记
        uniforms[name] = gl.getUniformLocation(program, name);
      }
    }
    return uniforms;
  }

  /**
   * 获取程序中所有 active attributes 的位置
   */
  static getAttributes(
    gl: WebGLRenderingContext,
    program: WebGLProgram
  ): { [key: string]: number } {
    const num = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
    const attributes: { [key: string]: number } = {};
    for (let i = 0; i < num; i++) {
      const info = gl.getActiveAttrib(program, i);
      if (info) {
        attributes[info.name] = gl.getAttribLocation(program, info.name);
      }
    }
    return attributes;
  }

  /**
   * 创建缓冲区（ARRAY_BUFFER 或 ELEMENT_ARRAY_BUFFER）
   */
  static createBuffer(
    gl: WebGLRenderingContext,
    data: number[] | ArrayBufferView,
    target: number = gl.ARRAY_BUFFER,
    usage: number = gl.STATIC_DRAW
  ): WebGLBuffer {
    const buffer = gl.createBuffer();
    if (!buffer) throw new Error('Failed to create buffer');

    gl.bindBuffer(target, buffer);
    const array = data instanceof Array ? new Float32Array(data) : data;
    // console.log("array", array);
    gl.bufferData(target, array, usage);
    gl.bindBuffer(target, null);

    return buffer;
  }

  /**
   * 创建索引缓冲区（ELEMENT_ARRAY_BUFFER）
   */
  static createIndexBuffer(
    gl: WebGLRenderingContext,
    indices: number[] | Uint16Array | Uint32Array
  ): WebGLBuffer {
    const array = indices instanceof Array ? new Uint16Array(indices) : indices;
    return this.createBuffer(gl, array, gl.ELEMENT_ARRAY_BUFFER);
  }

  /**
   * 将 attribute 关联到 buffer
   */
  static setAttribute(
    gl: WebGLRenderingContext,
    program: WebGLProgram,
    name: string,
    buffer: WebGLBuffer,
    size: number = 3,
    type: number = gl.FLOAT,
    normalized: boolean = false,
    stride: number = 0,
    offset: number = 0
  ): void {
    const location = gl.getAttribLocation(program, name);
    if (location === -1) return;

    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
    gl.enableVertexAttribArray(location);
  }

  /**
   * 设置 instanced attribute（WebGL2）
   */
  static setInstancedAttribute(
    gl: WebGL2RenderingContext,
    program: WebGLProgram,
    name: string,
    buffer: WebGLBuffer,
    size: number = 3,
    divisor: number = 1
  ): void {
    const location = gl.getAttribLocation(program, name);
    if (location === -1) return;

    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(location);
    gl.vertexAttribDivisor(location, divisor);
  }

  /**
   * 设置 Uniform 值（自动推断类型）
   */
  static setUniform(
    gl: WebGLRenderingContext,
    location: WebGLUniformLocation,
    value: number | number[] | Float32Array | Int32Array | WebGLTexture,
    textureUnit = 0
  ): void {
    if (typeof value === 'number') {
      gl.uniform1f(location, value);
    } else if (value instanceof WebGLTexture) {
      gl.activeTexture(gl.TEXTURE0 + textureUnit);
      gl.bindTexture(gl.TEXTURE_2D, value);
      gl.uniform1i(location, textureUnit);
    } else if (Array.isArray(value) || ArrayBuffer.isView(value)) {
      const arr = value as number[];
      if (arr.length === 2) gl.uniform2fv(location, arr);
      else if (arr.length === 3) gl.uniform3fv(location, arr);
      else if (arr.length === 4) gl.uniform4fv(location, arr);
      else if (arr.length === 9) gl.uniformMatrix3fv(location, false, arr);
      else if (arr.length === 16) gl.uniformMatrix4fv(location, false, arr);
      else gl.uniform1fv(location, arr);
    }
  }

  /**
   * 批量设置 Uniforms
   */
  static setUniforms(
    gl: WebGLRenderingContext,
    program: WebGLProgram,
    uniforms: { [key: string]: any },
    textureUnitStart = 0
  ): void {
    let textureUnit = textureUnitStart;
    for (const [name, value] of Object.entries(uniforms)) {
      const location = gl.getUniformLocation(program, name);
      if (location) {
        // console.log(`setUniform: ${name}`);
        this.setUniform(gl, location, value, textureUnit++);
      }
    }
  }

  /**
   * 创建 2D 纹理
   */
  static createTexture(
    gl: WebGLRenderingContext,
    source: TexImageSource | null = null,
    width = 1,
    height = 1,
    data: ArrayBufferView | null = null
  ): WebGLTexture {
    const texture = gl.createTexture();
    if (!texture) throw new Error('Failed to create texture');

    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

    if (source) {
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
    } else if (data) {
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
    } else {
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    }

    // Mipmap 或边缘处理
    if (source && isPowerOf2(width) && isPowerOf2(height)) {
      gl.generateMipmap(gl.TEXTURE_2D);
    } else {
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }

    gl.bindTexture(gl.TEXTURE_2D, null);
    return texture;
  }

  /**
   * 绑定纹理到 uniform
   */
  static setTexture(
    gl: WebGLRenderingContext,
    location: WebGLUniformLocation,
    texture: WebGLTexture,
    unit = 0
  ): void {
    gl.activeTexture(gl.TEXTURE0 + unit);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.uniform1i(location, unit);
  }

  /**
   * 创建帧缓冲区（可选绑定纹理）
   */
  static createFramebuffer(
    gl: WebGLRenderingContext,
    texture: WebGLTexture | null = null,
    level = 0,
    width = 128,
    height = 128
  ): WebGLFramebuffer {
    const framebuffer = gl.createFramebuffer();
    if (!framebuffer) throw new Error('Failed to create framebuffer');

    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

    if (texture) {
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, level);
    }

    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status !== gl.FRAMEBUFFER_COMPLETE) {
      console.error('Framebuffer is incomplete:', status);
      gl.deleteFramebuffer(framebuffer);
      throw new Error('Framebuffer creation failed');
    }

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    return framebuffer;
  }

  /**
   * 创建深度缓冲区并附加到帧缓冲
   */
  static createDepthRenderbuffer(
    gl: WebGLRenderingContext,
    framebuffer: WebGLFramebuffer,
    width: number,
    height: number
  ): WebGLRenderbuffer {
    const depthBuffer = gl.createRenderbuffer();
    if (!depthBuffer) throw new Error('Failed to create renderbuffer');

    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    return depthBuffer;
  }

  /**
   * 启用深度/模板测试
   */
  static enable(gl: WebGLRenderingContext, cap: number): void {
    gl.enable(cap);
  }

  /**
   * 设置模板测试为“始终通过，写入1”
   */
  static setStencilWrite(gl: WebGLRenderingContext): void {
    gl.stencilFunc(gl.ALWAYS, 1, 0xff);
    gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
    gl.stencilMask(0xff);
    gl.clear(gl.STENCIL_BUFFER_BIT);
    gl.colorMask(false, false, false, false);
  }

  /**
   * 设置模板测试为“等于1时通过”
   */
  static setStencilTest(gl: WebGLRenderingContext): void {
    gl.stencilFunc(gl.EQUAL, 1, 0xff);
    gl.stencilMask(0x00);
    gl.colorMask(true, true, true, true);
  }

  /**
   * 绘制图元
   */
  static draw(
    gl: WebGLRenderingContext,
    mode: number = gl.TRIANGLES,
    count: number,
    offset = 0,
    isElements = false
  ): void {
    if (isElements) {
      gl.drawElements(mode, count, gl.UNSIGNED_SHORT, offset);
    } else {
      gl.drawArrays(mode, offset, count);
    }
  }
}

// 默认导出
export default GlUtil;